import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import ttkbootstrap as ttkb
from ttkbootstrap.constants import *
import hashlib
import crcmod
import os
import binascii

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View

@annotate(ClassInfo(name="数据校验", category="tool"))
class CheckSumView(View):
    """设置视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 输入类型：file, text, hex
        self.input_type = tk.StringVar(value="text")
        # 校验算法
        self.checksum_type = tk.StringVar(value="md5")

        # 创建UI
        self._create_widgets()

    def _create_widgets(self):
        # 创建主框架
        main_frame = ttkb.Frame(self, padding=10)
        main_frame.pack(fill=BOTH, expand=True)

        # 输入方式选择
        input_frame = ttkb.LabelFrame(main_frame, text="输入方式", padding=10)
        input_frame.pack(fill=X, pady=(0, 10))

        # 输入方式单选按钮
        ttkb.Radiobutton(input_frame, text="文本字符", variable=self.input_type,
                         value="text", command=self.switch_input_mode).pack(side=LEFT, padx=10)
        ttkb.Radiobutton(input_frame, text="Hex字符串", variable=self.input_type,
                         value="hex", command=self.switch_input_mode).pack(side=LEFT, padx=10)
        ttkb.Radiobutton(input_frame, text="文件", variable=self.input_type,
                         value="file", command=self.switch_input_mode).pack(side=LEFT, padx=10)

        # 文件选择按钮（初始隐藏）
        self.file_frame = ttkb.Frame(input_frame)
        self.file_path_var = tk.StringVar(value="未选择文件")
        ttkb.Label(self.file_frame, textvariable=self.file_path_var, width=50).pack(side=LEFT, padx=5)
        ttkb.Button(self.file_frame, text="浏览...", command=self.select_file,
                    bootstyle=OUTLINE).pack(side=LEFT, padx=5)

        # 输入区域
        input_area_frame = ttkb.LabelFrame(main_frame, text="输入内容", padding=10)
        input_area_frame.pack(fill=BOTH, expand=True, pady=(0, 10))

        self.text_input = scrolledtext.ScrolledText(
            input_area_frame,
            wrap=tk.WORD,
            font=("Consolas", 10)
        )
        self.text_input.pack(fill=BOTH, expand=True)
        self.text_input.insert(tk.END, "请输入要计算校验值的文本...")

        # 校验方式选择
        checksum_frame = ttkb.LabelFrame(main_frame, text="校验方式", padding=10)
        checksum_frame.pack(fill=X, pady=(0, 10))

        # 校验算法选择
        algorithms = [
            ("CRC8", "crc8"),
            ("CRC16 (Modbus)", "crc16_modbus"),
            ("CRC16 (XMODEM)", "crc16_xmodem"),
            ("MD5", "md5"),
            ("SHA-1", "sha1"),
            ("SHA-256", "sha256"),
            ("SHA-512", "sha512")
        ]

        algo_frame = ttkb.Frame(checksum_frame)
        algo_frame.pack(fill=X)

        row, col = 0, 0
        for text, value in algorithms:
            ttkb.Radiobutton(algo_frame, text=text, variable=self.checksum_type,
                             value=value).grid(row=row, column=col, padx=10, pady=5, sticky=W)
            col += 1
            if col > 3:  # 每行显示4个选项
                col = 0
                row += 1

        # 计算按钮
        ttkb.Button(checksum_frame, text="计算校验值", command=self.calculate_checksum,
                    bootstyle=PRIMARY).pack(pady=10)

        # 结果显示区域
        result_frame = ttkb.LabelFrame(main_frame, text="校验结果", padding=10)
        result_frame.pack(fill=X, pady=(0, 10))

        result_inner_frame = ttkb.Frame(result_frame)
        result_inner_frame.pack(fill=X)

        self.result_var = tk.StringVar(value="")
        ttkb.Entry(result_inner_frame, textvariable=self.result_var, font=("Consolas", 10),
                   state="readonly").pack(side=LEFT, fill=X, expand=True, padx=5)
        ttkb.Button(result_inner_frame, text="复制结果", command=self.copy_result,
                    bootstyle=OUTLINE).pack(side=LEFT, padx=5)

        # 状态提示
        self.status_var = tk.StringVar(value="就绪")
        self.status_bar = ttkb.Label(self, textvariable=self.status_var, anchor=W)
        self.status_bar.pack(side=BOTTOM, fill=X)

        # 初始切换到文本模式
        self.switch_input_mode()

    def switch_input_mode(self):
        """切换输入模式"""
        mode = self.input_type.get()

        if mode == "file":
            # 文件模式
            self.text_input.pack_forget()
            self.file_frame.pack(side=LEFT, padx=10)
            self.status_var.set("请选择要计算校验值的文件")
        else:
            # 文本或Hex模式
            self.file_frame.pack_forget()
            self.text_input.pack(fill=BOTH, expand=True)

            if mode == "text":
                self.text_input.delete("1.0", tk.END)
                self.status_var.set("请输入文本，将计算其校验值")
            else:  # hex
                self.text_input.delete("1.0", tk.END)
                self.status_var.set("请输入Hex字符串（如：a1b2c3d4）...，将计算其校验值")

    def select_file(self):
        """选择文件"""
        file_path = filedialog.askopenfilename(
            title="选择文件",
            filetypes=[("所有文件", "*.*")]
        )

        if file_path:
            self.file_path_var.set(file_path)
            self.status_var.set(f"已选择文件: {os.path.basename(file_path)}")

    def calculate_checksum(self):
        """计算校验值"""
        try:
            # 获取输入数据
            input_mode = self.input_type.get()
            data = None

            if input_mode == "file":
                # 处理文件
                file_path = self.file_path_var.get()
                if file_path == "未选择文件":
                    messagebox.showwarning("警告", "请先选择文件")
                    return

                try:
                    with open(file_path, "rb") as f:
                        data = f.read()
                    self.status_var.set(f"已读取文件: {os.path.basename(file_path)}, 大小: {len(data)} 字节")
                except Exception as e:
                    messagebox.showerror("错误", f"读取文件失败: {str(e)}")
                    return

            elif input_mode == "hex":
                # 处理Hex字符串
                hex_str = self.text_input.get("1.0", tk.END).strip()
                # 移除所有空格
                hex_str = hex_str.replace(" ", "")

                if not hex_str:
                    messagebox.showwarning("警告", "请输入Hex字符串")
                    return

                try:
                    # 转换为字节
                    data = binascii.unhexlify(hex_str)
                    self.status_var.set(f"已解析Hex字符串，长度: {len(data)} 字节")
                except binascii.Error as e:
                    messagebox.showerror("错误", f"无效的Hex字符串: {str(e)}")
                    return

            else:  # text
                # 处理文本
                text = self.text_input.get("1.0", tk.END).rstrip('\n')  # 移除自动添加的换行符
                if not text:
                    messagebox.showwarning("警告", "请输入文本内容")
                    return

                # 转换为UTF-8字节
                data = text.encode('utf-8')
                self.status_var.set(f"已处理文本，长度: {len(text)} 字符, {len(data)} 字节")

            # 计算校验值
            checksum_type = self.checksum_type.get()
            result = ""

            if checksum_type == "crc8":
                # CRC8计算
                crc8 = crcmod.predefined.Crc('crc-8')
                crc8.update(data)
                result = f"{crc8.crcValue:02X}"

            elif checksum_type == "crc16_modbus":
                # CRC16 Modbus计算
                crc16 = crcmod.predefined.Crc('modbus')
                crc16.update(data)
                result = f"{crc16.crcValue:04X}"

            elif checksum_type == "crc16_xmodem":
                # CRC16 XMODEM计算
                crc16 = crcmod.predefined.Crc('xmodem')
                crc16.update(data)
                result = f"{crc16.crcValue:04X}"

            elif checksum_type == "md5":
                # MD5计算
                md5 = hashlib.md5()
                md5.update(data)
                result = md5.hexdigest()

            elif checksum_type == "sha1":
                # SHA-1计算
                sha1 = hashlib.sha1()
                sha1.update(data)
                result = sha1.hexdigest()

            elif checksum_type == "sha256":
                # SHA-256计算
                sha256 = hashlib.sha256()
                sha256.update(data)
                result = sha256.hexdigest()

            elif checksum_type == "sha512":
                # SHA-512计算
                sha512 = hashlib.sha512()
                sha512.update(data)
                result = sha512.hexdigest()

            # 显示结果
            self.result_var.set(result)
            self.status_var.set(f"已计算{self.get_checksum_name(checksum_type)}校验值")

        except Exception as e:
            messagebox.showerror("错误", f"计算校验值失败: {str(e)}")
            self.status_var.set(f"计算失败: {str(e)}")

    def get_checksum_name(self, checksum_type):
        """获取校验算法的名称"""
        names = {
            "crc8": "CRC8",
            "crc16_modbus": "CRC16 (Modbus)",
            "crc16_xmodem": "CRC16 (XMODEM)",
            "md5": "MD5",
            "sha1": "SHA-1",
            "sha256": "SHA-256",
            "sha512": "SHA-512"
        }
        return names.get(checksum_type, checksum_type)

    def copy_result(self):
        """复制结果到剪贴板"""
        result = self.result_var.get()
        if result:
            self.clipboard_clear()
            self.clipboard_append(result)
            self.status_var.set("校验结果已复制到剪贴板")
        else:
            self.status_var.set("没有可复制的校验结果")

    def create_widgets(self):
        pass
